﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PloobsEngine.Material;
using PloobsEngine;
using PloobsEngine.Modelo;
using PloobsEngine.SceneControl;
using PloobsEngine.Cameras;

namespace Engine.Material.Deferred
{

    public class WaterCompleteShader : IShader
    {
        
        private float specularIntensity = 0f;          
        private RenderTarget2D refractionRT;
        private RenderTarget2D reflectionRT;
        private Texture2D refractionMap;
        private Texture2D reflectionMap;
        private Texture2D normal0;
        private Texture2D normal1;
        private Effect _shader;          
        private float planeHeight;
        private int WIDTH;
        private int HEIGHT;
        private Plane plane;        
        private Matrix reflectiveViewMatrix;                
        private float timeModulation = 100;
        
        /// <summary>
        /// Default 100
        /// </summary>
        public float TimeModulation
        {
            get { return timeModulation; }
            set { timeModulation = value; }
        }
        private float waveLength = 0.2f;

        /// <summary>
        /// Default 0.2f
        /// </summary>
        public float WaveLength
        {
            get { return waveLength; }
            set { waveLength = value; }
        }
        private float waveHeight = 0.02f;

        /// <summary>
        /// Default 0.02f
        /// </summary>
        public float WaveHeight
        {
            get { return waveHeight; }
            set { waveHeight = value; }
        }

        private float windForce = 0.002f;

        /// <summary>
        /// Default 0.002f
        /// </summary>
        public float WindForce
        {
            get { return windForce; }
            set { windForce = value; }
        }
        private Vector4 waterColor = new Vector4(0.5f, 0.8f, 0.8f, 1.0f);

        /// <summary>
        /// Default Vector4(0.5f, 0.8f, 0.8f, 1.0f);
        /// </summary>
        public Color WaterColor
        {
            get { return new Color(waterColor); }
            set { waterColor = value.ToVector4(); }
        }
        private Vector3 windDirection = new Vector3(0, 0, 1);

        /// <summary>
        /// Default Vector3(0, 0, 1);
        /// </summary>
        public Vector3 WindDirection
        {
            get { return windDirection; }
            set { windDirection = value; }
        }


        public WaterCompleteShader(int width, int height, Plane plane, float Height)
        {
            this.WIDTH = width;
            this.HEIGHT = height;
            this.plane = plane;
            this.planeHeight = Height;
        }

        public WaterCompleteShader(int width, int height, Plane plane, float Height,bool affectedByLight)
        {
            this.WIDTH = width;
            this.HEIGHT = height;
            this.plane = plane;
            this.planeHeight = Height;
            if (affectedByLight)
                this.shaderId = 0;
            else
                this.shaderId = 0.95f;
        }
      
        public float SpecularIntensity
        {
            get { return specularIntensity; }
            set { specularIntensity = value; }
        }
        private float specularPower = 0f;

        public float SpecularPower
        {
            get { return specularPower; }
            set { specularPower = value; }
        }

        private Plane CreatePlane(float height, Vector3 planeNormalDirection, Matrix currentViewMatrix, bool clipSide,ICamera cam)
        {
            planeNormalDirection.Normalize();
            Vector4 planeCoeffs = new Vector4(planeNormalDirection, height);
            if (clipSide)
                planeCoeffs *= -1;

            Matrix worldViewProjection = currentViewMatrix * cam.Projection;
            Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
            inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);

            planeCoeffs = Vector4.Transform(planeCoeffs, inverseWorldViewProjection);
            Plane finalPlane = new Plane(planeCoeffs);

            return finalPlane;
        }

        


        public override void PreDrawPhase(IWorld mundo, IModelo modelo, IRenderHelper render, ICamera cam)
        {            
            ///REFRACAO
            Plane refractionClipPlane;
            if (cam.Position.Y > obj.Position.Y)
            {            
                refractionClipPlane = CreateReflectionPlane(plane.D, plane.Normal, cam.View, cam.Projection, true);
            }
            else
            {
                refractionClipPlane = CreateReflectionPlane(plane.D, plane.Normal, cam.View, cam.Projection, false);
            }
            EngineStuff.GraphicsDevice.ClipPlanes[0].Plane = refractionClipPlane;
            EngineStuff.GraphicsDevice.ClipPlanes[0].IsEnabled = true;
            EngineStuff.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
            EngineStuff.GraphicsDevice.RenderState.DepthBufferEnable = true;
            EngineStuff.GraphicsDevice.SetRenderTarget(0, refractionRT);
            EngineStuff.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
            render.RenderSceneWithoutMaterial(mundo, new List<IObject>() { obj }, cam.View, cam.Projection, true);
            EngineStuff.GraphicsDevice.ClipPlanes[0].IsEnabled = false;
            EngineStuff.GraphicsDevice.SetRenderTarget(0, null);
            refractionMap = refractionRT.GetTexture();
            //refractionMap.Save("teste2.png", ImageFileFormat.Png);

            ///REFLEXAO
            Matrix m = Matrix.CreateReflection(plane);
            Vector3 pos;
            Vector3 target;
            Vector3 up;
            pos = Vector3.Transform(cam.Position, m);
            target = Vector3.Transform(cam.Target, m);
            up = Vector3.Transform(cam.Up, m);
            reflectiveViewMatrix = Matrix.CreateLookAt(pos, target, up);
            Plane reflectionClipPlane = CreateReflectionPlane(plane.D , plane.Normal, reflectiveViewMatrix, cam.Projection, false);
            EngineStuff.GraphicsDevice.ClipPlanes[0].Plane = reflectionClipPlane;
            EngineStuff.GraphicsDevice.ClipPlanes[0].IsEnabled = true;
            EngineStuff.GraphicsDevice.SetRenderTarget(0, reflectionRT);
            EngineStuff.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
            EngineStuff.GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;
            render.RenderSceneWithoutMaterial(mundo, new List<IObject>() { obj }, reflectiveViewMatrix, cam.Projection,true);
            EngineStuff.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            EngineStuff.GraphicsDevice.SetRenderTarget(0, null);
            reflectionMap = reflectionRT.GetTexture();
            EngineStuff.GraphicsDevice.ClipPlanes[0].IsEnabled = false;
            //reflectionMap.Save("teste3.png", ImageFileFormat.Png);            

            ///precisa, mas nao devia ...
            EngineStuff.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
        }               

        public override void Draw(IModelo modelo, IRenderHelper render, ICamera camera)
        {
            if (obj != null)
            {
                float time = (float)EngineStuff.GetCurrentGameTime().TotalGameTime.TotalMilliseconds / timeModulation;

                this._shader.Parameters["specularIntensity"].SetValue(specularIntensity);
                this._shader.Parameters["specularPower"].SetValue(specularPower);
                this._shader.Parameters["View"].SetValue(camera.View);
                this._shader.Parameters["camPos"].SetValue(camera.Position);
                this._shader.Parameters["ReflectionView"].SetValue(reflectiveViewMatrix);
                this._shader.Parameters["RefractionView"].SetValue(camera.View);
                this._shader.Parameters["ReflectionMap"].SetValue(reflectionMap);
                this._shader.Parameters["RefractionMap"].SetValue(refractionMap);
                this._shader.Parameters["normalMap0"].SetValue(normal0);
                this._shader.Parameters["normalMap1"].SetValue(normal1);
                this._shader.Parameters["xWaveLength"].SetValue(waveLength);
                this._shader.Parameters["xWaveHeight"].SetValue(waveHeight);
                this._shader.Parameters["Time"].SetValue(time);
                this._shader.Parameters["WindForce"].SetValue(windForce);
                this._shader.Parameters["waterColor"].SetValue(waterColor);
                this._shader.Parameters["WindDirection"].SetValue(windDirection);
                this._shader.Parameters["Projection"].SetValue(camera.Projection);
                this._shader.Parameters["id"].SetValue(shaderId);

                EngineStuff.GraphicsDevice.RenderState.CullMode = CullMode.None;
                

                Matrix[] boneTransforms = modelo.getBonesTransformation();
                Matrix wld = obj.getWorldMatrix();
                for (int i = 0; i < modelo.MeshNumber; i++)
                {
                    BatchInformation[] bi = modelo.GetBatchInformation(i);
                    Matrix w1 = Matrix.Multiply(wld, boneTransforms[modelo.GetParentBoneIndex(i)]);
                    this._shader.Parameters["World"].SetValue(w1);
                    for (int j = 0; j < bi.Count(); j++)
                    {

                        this._shader.Begin();
                        EngineStuff.GraphicsDevice.VertexDeclaration = bi[j].VertexDeclaration;
                        EngineStuff.GraphicsDevice.Vertices[0].SetSource(modelo.GetVertexBuffer(i), bi[j].StreamOffset, bi[j].VertexStride);
                        EngineStuff.GraphicsDevice.Indices = modelo.GetIndexBuffer(i);

                        this._shader.CurrentTechnique.Passes[0].Begin();
                        EngineStuff.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, bi[j].BaseVertex, 0, bi[j].NumVertices, bi[j].StartIndex, bi[j].PrimitiveCount);
                        this._shader.CurrentTechnique.Passes[0].End();
                        this._shader.End();
                    }

                }               
                 
            }
            EngineStuff.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
        }

        private Plane CreateReflectionPlane(float height, Vector3 planeNormalDirection, Matrix currentViewMatrix, Matrix camProj ,bool clipSide)
        {
            planeNormalDirection.Normalize();
            Vector4 planeCoeffs = new Vector4(planeNormalDirection, height);
            if (clipSide)
                planeCoeffs *= -1;

            Matrix worldViewProjection = currentViewMatrix * camProj;
            Matrix inverseWorldViewProjection = Matrix.Invert(worldViewProjection);
            inverseWorldViewProjection = Matrix.Transpose(inverseWorldViewProjection);

            planeCoeffs = Vector4.Transform(planeCoeffs, inverseWorldViewProjection);
            Plane finalPlane = new Plane(planeCoeffs);

            return finalPlane;
        }

        public override void Initialize()
        {
            this._shader = EngineStuff.InternalContentManager.GetAsset<Effect>("WaterComplete");
            refractionRT = EngineStuff.GetDefaultColorBuffer(WIDTH,HEIGHT);
            reflectionRT = EngineStuff.GetDefaultColorBuffer(WIDTH, HEIGHT);
            normal0 = EngineStuff.InternalContentManager.GetAsset<Texture2D>("wave0");
            normal1 = EngineStuff.InternalContentManager.GetAsset<Texture2D>("wave1");                                    
        }

        public override MaterialType MaterialType
        {
            get { return MaterialType.DEFERRED; }
        }
    }
}
